#include "Game.hpp"

Game::~Game()
{
    Network::quit();
    if (render != nullptr)
    {
        SDL_DestroyRenderer(render);
    }
    Utils::quitSDL();
    printf("Game closed\n");
}

Network &Game::getNetwork()
{
    return net;
}

int Game::run()
{
    if (Utils::initSDL())
    {
    }

    if (Network::init())
    {
        SDL_Log("Network init falied");
        return 1;
    }
    Network::setSocketInfo(AF_INET, SOCK_STREAM, 0);
    if (!net.createSocket())
    {
        SDL_Log("Network createSocket falied");
    }

    // unsigned long ul = 1;

    // int ret = ioctlsocket(net.getSocket(), FIONBIO, (unsigned long *)&ul); // 设置成非阻塞模式。

    // if (ret == SOCKET_ERROR) // 设置失败。
    // {
    //     SDL_Log("Network createSocket falied 2");
    // }
    return this->mainMenu();
}

int Game::mainMenu()
{
    std::cout << "是否是服务器? (y/n):";
    char ch = std::cin.get();
    if (ch == 'y' || ch == 'Y')
    {
        m_server = true;
        return this->server();
    }
    else
    {
        m_server = false;
        return this->cilent();
    }
    return 0;
}

int Game::server()
{
    unsigned short port = 4800;
    if (!net.bind(port))
    {
        return 1;
    }
    if (!net.listen(1))
    {
        return 1;
    }
    std::cout << "服务器开启....端口为:" << port << "\n";
    NetworkMsg msg;
    while (!exited)
    {
        if (net.accept(&msg))
        {
            con_closed = false;
            std::cout << inet_ntoa(msg.sock_addr.sin_addr) << " connected\n";
            transfrom_socket = msg.socket;
            std::thread th_recv(&Game::thread_recv, this, transfrom_socket);
            gameLoop();
            th_recv.join();
        };
    }

    return 0;
}

int Game::cilent()
{
    char ip[15] = "192.168.31.171";
    unsigned short port = 4800;
    while (!exited)
    {
        std::cout << "正在等待来自 " << ip << " 的回应\n";
        if (net.connect(ip, port))
        {
            transfrom_socket = net.getSocket();
            std::cout << "连接成功\n";
            std::thread th_recv(&Game::thread_recv, this, transfrom_socket);
            gameLoop();
            th_recv.join();
        }
        else
        {
            std::cout << "连接" << ip << " 失败\n";
        }
    }

    return 0;
}

int Game::gameLoop()
{
    window = SDL_CreateWindow("Gobang", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, win_width, win_height, SDL_WINDOW_SHOWN);
    render = SDL_CreateRenderer(window, -1, 0);
    Utils::initResources(render);
    SDL_Event evt;
    memset(map, 0, 225);
    for (int i = 0; i < 4; i++)
    {
        map[i] = 1;
    }
    player = -1;
    if (m_server)
    {
        srand(time(0));
        if (rand() % 2)
        {
            myturn = true;
            player = 1;
            printf("你的黑棋先手\n");
        }
        else
        {
            myturn = 0;
            player = -1;
            GameMsg gmsg;
            gmsg.type = GameMsg::YOU_FIRST;
            net.send(transfrom_socket, (char *)&gmsg, sizeof(gmsg), 0);
        }
    }
    else
    {
        myturn = 0;
    }

    rest_time = default_time;
    std::thread th_timer([&](Game *gm)
                         {
        int overtime_count=0;
    while(!gm->exited && !gm->win){
        if(gm->rest_time>0){
            gm->rest_time--;
        }else if(!gm->myturn){
            overtime_count++;
            GameMsg gmsg={0};
            gmsg.type=GameMsg::OVERTIME;
            gm->getNetwork().send(gm->transfrom_socket,(char*)&gmsg,sizeof(gmsg),0);
        if(overtime_count>=5){
                printf("你赢了\n");
                gm->win = true;
                overtime_count=0;
            }
        }
        GameMsg gmsg={0};
        gmsg.type=GameMsg::TOUCH;
        gm->getNetwork().send(gm->transfrom_socket,(char*)&gmsg,sizeof(gmsg),0);
        gm->touch_count++;
        if(gm->touch_count>=5){
            printf("对方断开连接\n");
            gm->win=true;
        }
        std::this_thread::sleep_for(std::chrono::seconds(1));
    } }, this);
    while (!exited)
    {
        while (SDL_PollEvent(&evt))
        {
            if (evt.type == SDL_QUIT)
            {
                exited = true;
                shutdown(transfrom_socket, SD_BOTH);
                closesocket(transfrom_socket);
                shutdown(net.getSocket(), SD_BOTH);
                closesocket(net.getSocket());
                break;
            }
            else if (evt.type == SDL_MOUSEMOTION)
            {
                x = evt.motion.x;
                y = evt.motion.y;
            }
            else if (!win && myturn && evt.type == SDL_MOUSEBUTTONDOWN && evt.button.button == SDL_BUTTON_LEFT)
            {
                if (down != -1 && map[down] == 0)
                {
                    chess_down();
                    myturn = 0;
                }
            }
        }
        gameRender();
        std::this_thread::sleep_for(std::chrono::milliseconds(30));
    }
    th_timer.join();
    return 0;
}

void Game::gameRender()
{
    SDL_SetRenderDrawColor(render, 0xf6, 0xce, 0x96, 0);
    SDL_RenderFillRect(render, nullptr);
    SDL_SetRenderDrawBlendMode(render, SDL_BLENDMODE_NONE);
    drawBoard();
    mouseHover();
    SDL_FRect rect;
    if (win)
    {

        rect.w = block_size;
        rect.h = block_size;
        x = win_draw_info[0] % 15;
        y = win_draw_info[0] / 15;
        int dx = win_draw_info[1];
        int dy = win_draw_info[2];
        int count = win_draw_info[3];
        SDL_SetRenderDrawBlendMode(render, SDL_BLENDMODE_BLEND);
        for (int i = 0; i < count; i++)
        {
            rect.x = padding + (x + i * dx) * block_size - rect.w / 2;
            rect.y = padding + (y + i * dy) * block_size - rect.h / 2;
            SDL_SetRenderDrawColor(render, 0xff, 0, 0, 0xa0);
            SDL_RenderFillRectF(render, &rect);
        }
    }
    rect.w = chess_size;
    rect.h = chess_size;
    for (int i = 0; i < 225; i++)
    {
        if (!map[i])
            continue;
        rect.x = padding + i % row * block_size - chess_size / 2;
        rect.y = padding + i / row * block_size - chess_size / 2;
        if (map[i] == CHESS_BLACK)
        {
            SDL_SetTextureAlphaMod(Utils::tex_black, 0xff);
            SDL_RenderCopyF(render, Utils::tex_black, nullptr, &rect);
        }
        else if (map[i] == CHESS_WHITE)
        {
            SDL_SetTextureAlphaMod(Utils::tex_white, 0xff);
            SDL_RenderCopyF(render, Utils::tex_white, nullptr, &rect);
        }
    }

    if (last_step != -1)
    {
        rect.x = padding + last_step % 15 * block_size - chess_size / 2;
        rect.y = padding + last_step / 15 * block_size - chess_size / 2;
        SDL_SetRenderDrawColor(render, 255, 0, 0, 0xff);
        SDL_RenderDrawRectF(render, &rect);
    }
    SDL_Rect rc = {600, 20, 0, 0};
    wchar_t str[20];
    SDL_Color col = {255, 0, 0, 255};

    if (myturn)
    {
        wsprintfW(str, L"你的回合:%d秒", rest_time);
        col.r = 0;
        col.b = 0xff;
    }
    else
    {
        wsprintfW(str, L"对方的回合:%d秒", rest_time);
    }
    SDL_SetRenderDrawBlendMode(render, SDL_BLENDMODE_NONE);
    SDL_Surface *sur_text = TTF_RenderUNICODE_Blended(Utils::font, (Uint16 *)str, col);
    SDL_Texture *tex_text = SDL_CreateTextureFromSurface(render, sur_text);
    TTF_SizeUNICODE(Utils::font, (Uint16 *)str, &rc.w, &rc.h);
    SDL_RenderCopy(render, tex_text, nullptr, &rc);
    SDL_FreeSurface(sur_text);
    SDL_DestroyTexture(tex_text);
    SDL_RenderPresent(render);
}

void Game::drawBoard()
{
    SDL_FRect rect;
    rect.h = true_size + line_width / 2;
    rect.w = line_width;
    rect.y = padding;
    SDL_SetRenderDrawBlendMode(render, SDL_BLENDMODE_NONE);
    SDL_SetRenderDrawColor(render, 0, 0, 0, 0);
    for (int i = 0; i < row; i++)
    {
        rect.x = padding + i * block_size - line_width / 2;
        SDL_RenderFillRectF(render, &rect);
    }
    rect.h = line_width;
    rect.w = true_size + line_width / 2;
    rect.x = padding;
    SDL_SetRenderDrawColor(render, 0, 0, 0, 0);
    for (int i = 0; i < row; i++)
    {
        rect.y = padding + i * block_size - line_width / 2;
        SDL_RenderFillRectF(render, &rect);
    }
    const SDL_Point ps[5] = {{7, 7}, {3, 3}, {11, 11}, {3, 11}, {11, 3}};
    const float mark_size = chess_size / 2;
    rect.w = mark_size;
    rect.h = mark_size;
    SDL_SetTextureAlphaMod(Utils::tex_black, 0xff);
    for (int i = 0; i < 5; i++)
    {
        rect.x = padding + ps[i].x * block_size - mark_size / 2;
        rect.y = padding + ps[i].y * block_size - mark_size / 2;
        SDL_RenderCopyF(render, Utils::tex_black, nullptr, &rect);
    }
}

void Game::mouseHover()
{
    SDL_FRect rect;
    rect.w = chess_size;
    rect.h = chess_size;
    if (myturn && !win && x >= 0 && x <= board_size && y >= 0 && y <= board_size)
    {
        x = x < padding ? padding : x;
        x = x > true_size + padding ? true_size + padding : x;
        y = y < padding ? padding : y;
        y = y > true_size + padding ? true_size + padding : y;
        int dx = (x - padding) / block_size + 0.5;
        int dy = (y - padding) / block_size + 0.5;
        down = dy * row + dx;
        rect.x = padding + dx * block_size - chess_size / 2;
        rect.y = padding + dy * block_size - chess_size / 2;
        if (player == 1)
        {
            SDL_SetTextureAlphaMod(Utils::tex_black, 0xa0);
            SDL_RenderCopyF(render, Utils::tex_black, nullptr, &rect);
        }
        else
        {
            SDL_SetTextureAlphaMod(Utils::tex_white, 0xa0);
            SDL_RenderCopyF(render, Utils::tex_white, nullptr, &rect);
        }
    }
    else
    {
        down = -1;
    }
}

void Game::thread_recv(Game *gm, SOCKET socket)
{
    std::cout << "start recv\n";
    NetworkMsg msg;
    while (!gm->exited)
    {
        int res = gm->getNetwork().recv(socket, msg.msg, 0xff, 0);
        if (res > 0)
        {
            GameMsg *gmsg = (GameMsg *)msg.msg;
            if (gmsg->type & GameMsg::CHESS_DOWN)
            {
                int down = (*(int *)gmsg->data);
                printf("%s: CHESS_DOWN (down=%d)\n", inet_ntoa(msg.sock_addr.sin_addr), down);
                gm->map[down] = -gm->player;
                gm->myturn = true;
                gm->last_step = down;
                gm->rest_time = gm->default_time;
                printf("map[%d]=%d\n", down, gm->map[down]);
            }
            if (gmsg->type & GameMsg::CHECK_WIN && gm->myturn)
            {
                if (gm->check_win(gm->last_step, -gm->player))
                {
                    printf("你输了\n");
                    gm->win = true;
                    GameMsg gmsg = {0};
                    gmsg.type |= GameMsg::CONFIRM_WIN;
                    gm->getNetwork().send(gm->transfrom_socket, (char *)&gmsg, sizeof(gmsg), 0);
                }
            }
            if (gmsg->type & GameMsg::CONFIRM_WIN)
            {
                printf("你赢了\n");
                gm->win = true;
            }
            if (gmsg->type & GameMsg::YOU_FIRST)
            {
                gm->myturn = true;
                gm->player = 1;
                printf("你的黑棋先手\n");
                gm->rest_time = gm->default_time;
            }
            if (gmsg->type & GameMsg::OVERTIME && gm->rest_time <= 0)
            {
                printf("超时,你输了\n");
                gm->win = true;
                GameMsg gmsg = {0};
                gmsg.type |= GameMsg::CONFIRM_WIN;
                gm->getNetwork().send(gm->transfrom_socket, (char *)&gmsg, sizeof(gmsg), 0);
            }
            if(gmsg->type & GameMsg::TOUCH){
                gm->touch_count=0;
            }
        }
        else if (res == 0)
        {
            printf("%s:disconnected\n", inet_ntoa(msg.sock_addr.sin_addr));
            gm->exited = true;
            break;
        }
    }
    std::cout << "end recv\n";
}

bool Game::check_win(int down, int player)
{
    int dir[4][2] = {
        0, 1,
        1, 0,
        1, 1,
        1, -1};
    int x = down % 15;
    int y = down / 15;
    for (int i = 0; i < 4; i++)
    {
        int left = contact_count(player, down, dir[i], false);
        int right = contact_count(player, down, dir[i], true);
        if (left + right >= 4)
        {
            int win_begin_x = x + dir[i][0] * left;
            int win_begin_y = y + dir[i][1] * left;
            win_draw_info[0] = win_begin_y * 15 + win_begin_x;
            win_draw_info[1] = dir[i][0] * -1;
            win_draw_info[2] = dir[i][1] * -1;
            win_draw_info[3] = left + right + 1;
            win_draw_info[4] = player;
            return true;
        }
    }
    return false;
}

int Game::contact_count(int player, int down, int dir[2], bool reverse)
{

    int count = 0;
    int rx, ry;
    int dx = reverse ? -1 * dir[0] : dir[0];
    int dy = reverse ? -1 * dir[1] : dir[1];
    int x = down % 15;
    int y = down / 15;
    for (int i = 1; i < 5; i++)
    {
        rx = x + dx * i;
        ry = y + dy * i;
        if (rx < 0 || rx >= 225 || ry < 0 || ry >= 225)
            break;
        if (map[ry * 15 + rx] == player)
            count++;
        else
        {
            break;
        }
    }
    return count;
}

void Game::chess_down()
{
    map[down] = player;
    last_step = down;
    GameMsg msg = {0};
    msg.type = GameMsg::CHESS_DOWN;
    int *data_int = (int *)msg.data;
    data_int[0] = down;
    printf("map[%d]=%d\n", down, map[down]);
    rest_time = default_time;
    if (check_win(down, player))
    {
        msg.type |= GameMsg::CHECK_WIN;
    }
    net.send(transfrom_socket, (const char *)&msg, 0xff, 0);
}
